#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import argparse
import json
import re
import subprocess
from datetime import datetime

import AutoExecUtils


class IpmiManager:

    def __init__(self, host, username, password, isVerbose):
        self.host = host
        self.username = username
        self.password = password
        self.isVerbose = isVerbose

    def runCmd(self, cmd, skipLine=None):
        runCmd = "ipmitool -I lanplus -H {} -U {} -P {} {}".format(self.host, self.username, self.password, cmd)
        viewCmd = "ipmitool -I lanplus -H {} -U {} -P {} {}".format(self.host, self.username, "******", cmd)
        print("INFO:: Exec command ", viewCmd)
        child = subprocess.Popen(runCmd, shell=True, close_fds=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        if skipLine is None:
            skipLine = 0

        result = None
        while True:
            line = child.stdout.readline(4096)
            if not line:
                break

            if (skipLine > 0):
                skipLine = skipLine - 1
                continue

            line = line.strip()
            line = line.decode('utf-8')
            if result is None:
                result = line
            else:
                result = result + "\n" + line

        # 把error错误，追加到cmd最后
        count = 0
        while True:
            if child.stderr is None:
                break

            line = child.stderr.readline(4096)
            if not line:
                break

            line = line.strip()
            line = line.decode('utf-8')
            if result is None:
                result = line
            elif count == 0:
                result = result + line
            else:
                result = result + "\n" + line
            count = count+1

        if (self.isVerbose == 1):
            print("INFO::", result)
        
        if(result is not None and 'Unable to establish IPMI' in result ) :
            print("ERROR:: Not allow exec command , Maybe user account and password wrong or account permission denied.")
            exit(1)
        return result

    # 传感器列表
    def getSdrList(self):
        result = self.runCmd("sdr type list", 1)
        '''
        Sensor Types:
            Temperature               (0x01)   Voltage                   (0x02)
            Current                   (0x03)   Fan                       (0x04)
            Physical Security         (0x05)   Platform Security         (0x06)
            Processor                 (0x07)   Power Supply              (0x08)
            Power Unit                (0x09)   Cooling Device            (0x0a)
            Other                     (0x0b)   Memory                    (0x0c)
            Drive Slot / Bay          (0x0d)   POST Memory Resize        (0x0e)
            System Firmwares          (0x0f)   Event Logging Disabled    (0x10)
            Watchdog1                 (0x11)   System Event              (0x12)
            Critical Interrupt        (0x13)   Button                    (0x14)
            Module / Board            (0x15)   Microcontroller           (0x16)
            Add-in Card               (0x17)   Chassis                   (0x18)
            Chip Set                  (0x19)   Other FRU                 (0x1a)
            Cable / Interconnect      (0x1b)   Terminator                (0x1c)
            System Boot Initiated     (0x1d)   Boot Error                (0x1e)
            OS Boot                   (0x1f)   OS Critical Stop          (0x20)
            Slot / Connector          (0x21)   System ACPI Power State   (0x22)
            Watchdog2                 (0x23)   Platform Alert            (0x24)
            Entity Presence           (0x25)   Monitor ASIC              (0x26)
            LAN                       (0x27)   Management Subsys Health  (0x28)
            Battery                   (0x29)   Session Audit             (0x2a)
            Version Change            (0x2b)   FRU State                 (0x2c)
        '''
        sdrMap = {
            'Temperature': '0x01',
            'Voltage': '0x02',
            'Current': '0x03',
            'Fan': '0x04',
            'Processor': '0x07',
            'Power Supply': '0x08',
            'Power Unit': '0x09',
            'Memory': '0x0c',
            'Drive Slot / Bay': '0x0d',
            'Cable / Interconnect': '0x1b'
        }

        if len(result) == 0:
            return None

        rsMap = {}
        rs = result.split('\n')
        for line in rs:
            if line == '':
                continue
            info = re.split(r'\s\s+', line)
            rsMap[info[0].strip()] = (info[1].strip()).replace('(', '').replace(')', '')
            if (len(info) > 2):
                rsMap[info[2].strip()] = (info[3].strip()).replace('(', '').replace(')', '')

        for key in sdrMap:
            rsVal = rsMap[key]
            if rsVal is not None and sdrMap[key] != rsVal:
                rsMap[key] = rsVal

        #处理key 
        newSdrMap = {}
        for key in sdrMap:
            value = sdrMap[key]
            if ('/' in key ):
                slen = key.index('/')
                key = key[0:slen].strip()
            if (' ' in key ) : 
                key = key.strip().replace(' ', '_')
            newSdrMap[key] = value
        return newSdrMap

    # 按空格行切分
    def split_blank_lines(self, s):
        blank_line_regex = r"(?:\r?\n){2,}"
        return re.split(blank_line_regex, s.strip())

    # 传感器明细数据
    def getSdrDetail(self, cmd):
        sdrList = []
        result = self.runCmd("sdr type {} -v".format(cmd))
        if result is None:
            return sdrList
        rs = self.split_blank_lines(result.strip())
        for block in rs:
            ins = {}
            for line in block.split('\n'):
                info = line.split(':')
                if len(info) > 1:
                    ins[info[0].strip()] = info[1].strip()
            sdrList.append(ins)
        return sdrList

    # 传感器数据
    def getSdr(self, cmd, type):
        sdrList = []
        result = self.runCmd("sdr type {} ".format(cmd))
        if result is None:
            return sdrList

        rs = result.split('\n')
        for line in rs:
            ins = {}
            info = line.split('|')
            if len(info) > 1:
                ins['SENSOR_ID'] = info[0].strip()
                ins['ENTITY_ID'] = info[3].strip()
                if (type == "Temperature"):
                    value = info[4].strip().replace('degrees C', '').replace('unspecified' , '')
                    try:
                        value = int(value)
                    except Exception as ex:
                        value = 0
                    ins['SENSOR_READING'] = value
                else:
                    ins['SENSOR_READING'] = info[4].strip()
                ins['STATUS'] = info[2].strip()
                sdrList.append(ins)
        return sdrList

    # 可替换部件
    def getFru(self, data):
        fruList = []
        result = self.runCmd("fru")
        if result is None:
            return fruList

        rs = self.split_blank_lines(result.strip())
        for block in rs:
            if ('Unknown FRU header' in block or 'Device not present' in block):
                print("WARN::", block)
                continue

            ins = {}
            for line in block.split('\n'):
                info = line.split(':')
                if len(info) < 1:
                    continue

                key = info[0].strip()
                value = info[1].strip()

                if('FRU Device Description' in key):
                    ins['FRU_DEVICE'] = value

                elif('Board Mfg' in key):
                    ins['BOARD_MFG'] = value
                elif('Board Product' in key):
                    ins['BOARD_PRODUCT'] = value
                elif('Board Serial' in key):
                    ins['BOARD_SERIAL'] = value
                elif('Board Part Number' in key):
                    ins['BOARD_NUMBER'] = value

            if 'BOARD_NUMBER' not in ins:
                ins['BOARD_NUMBER'] = ''
            if 'BOARD_SERIAL' not in ins:
                ins['BOARD_SERIAL'] = ''
            if 'BOARD_PRODUCT' not in ins:
                ins['BOARD_PRODUCT'] = ''
            if 'BOARD_MFG' not in ins:
                ins['BOARD_MFG'] = ''
            fruList.append(ins)

            # 主板
            if '(ID 0)' in ins['FRU_DEVICE']:
                data['BOARD_PRODUCT'] = ins['BOARD_PRODUCT']
                data['BOARD_NUMBER'] = ins['BOARD_NUMBER']
                data['BOARD_SERIAL'] = ins['BOARD_SERIAL']
                data['MANUFACTURER'] = ins['BOARD_MFG']

        return fruList

    # 主板状态
    def getChassisStatus(self, ins):
        result = self.runCmd('chassis status')
        rs = result.split('\n')
        for line in rs:
            info = line.split(':')
            key = info[0].strip()
            if ( 'System Power' in key ) :
                ins['STATE'] = info[1].strip()
        return ins

    # 基础信息
    def getBaseInfo(self):
        ins = {}
        result = self.runCmd('lan print')
        rs = result.split('\n')
        for line in rs:
            info = line.split(":")
            if ( len(info) > 1
            and 'IP Address' in line
            or 'Subnet Mask' in line
            or 'Default Gateway IP' in line
            or 'MAC Address' in line
            ) :
                slen = line.index(":")
                key = line[0:slen].strip().replace(' ', '_').upper()
                value = line[slen+1:].strip()
                ins[key] = value

        result = self.runCmd('mc info')
        rs = result.split('\n')
        for line in rs:
            info = line.split(':')
            key = info[0].strip()
            key2 = key.replace(' ', '_').upper()
            if ('Firmware Revision' in key):
                ins[key2] = info[1].strip()
        return ins

    def transDate(self, str ,parsefrm , outfrm ) :
        strDate = datetime.strptime(str, parsefrm).date()
        fmtstr = datetime.strftime(strDate, outfrm)
        return fmtstr

    # 系统日志
    def getSel(self):
        selIns = {}
        result = self.runCmd("sel info")
        if result is None:
            return None
        rs = result.split('\n')
        for line in rs:
            info = line.split(":")
            if len(info) > 1 :
                slen = line.index(":")
                key = line[0:slen].strip().replace(' ', '_').upper()
                value = line[slen+1:].strip()
                if ( 'Last Add Time' in line or 'Last Del Time' in line ) :
                    value = self.transDate(value , "%m/%d/%Y %H:%M:%S" , "%Y-%m-%d %H:%M:%S")
                elif( 'Percent Used' in line ) :
                    value = int(value.replace('%' , ''))
                selIns[key] = value
        return selIns
    
    def getSelDetail(self):
        selList = []
        rslist = self.runCmd("sel list")
        if rslist is None:
            return selList
        rs = rslist.split('\n')
        total = len(rs)
        num = 0
        for line in rs:
            num = num + 1
            # 只保留最后的20行数据
            if (total > 20 and total - num > 20):
                continue
            ins = {}
            info = line.split('|')
            if len(info) > 1:
                date = info[1].strip()
                time = info[2].strip()
                event = info[3].strip()
                message = info[4].strip()
                type = info[5].strip()
                ins['DATE'] = self.transDate(date , "%m/%d/%Y" , "%Y-%m-%d")
                ins['TIME'] = time
                ins['EVENT'] = event
                ins['MESSAGE'] = message
                ins['TYPE'] = type
                selList.append(ins)
        return selList


def usage():
    pname = os.path.basename(__file__)
    print(pname + " --node <host node> --user <user> --password <password> --verbose")
    exit(1)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--user', default='', help='default user ')
    parser.add_argument('--password', default='', help='default user password')
    parser.add_argument('--verbose', default='0', help='verbose output')

    args = parser.parse_args()
    isVerbose = int(args.verbose)
    node = os.getenv('AUTOEXEC_NODE')
    if node != None and node != '':
        node = json.loads(node)

    host = ''
    user = ''
    password = ''
    port = 623
    if (node != None):
        host = node['host']
        if 'port' in node:
            port = node['port']

        if 'username' in node:
            user = node['username']
        else:
            user = args.user

        if 'password' in node:
            password = node['password']
        else:
            password = args.password

    if (host == '' or user == '' or password == ''):
        usage()

    autoexecHome = os.environ.get('AUTOEXEC_HOME')
    os.environ['PATH'] = '%s:%s/tools' % (os.environ['PATH'], autoexecHome)

    ipmi = IpmiManager(host, user, password, isVerbose)
    data = ipmi.getBaseInfo()
    data['MGMT_IP'] = host
    data['_OBJ_CATEGORY'] = 'SERVERDEV'
    data['_OBJ_TYPE'] = 'SERVERDEV'
    data['MGMT_PORT'] = port
    data['PK'] = ["MGMT_IP"]

    ipmi.getChassisStatus(data)
    sdrMap = ipmi.getSdrList()
    for key in sdrMap:
        rlist = ipmi.getSdr(sdrMap[key], key)

        data[key.upper()] = rlist
    fru = ipmi.getFru(data)
    data['FRU'] = fru
    sel = ipmi.getSel()
    data['SEL'] = sel
    selDetail = ipmi.getSelDetail()
    data['SEL_LIST'] = selDetail

    slist = []
    slist.append(data)

    if(isVerbose == 1):
        print(slist)

    out = {}
    out['DATA'] = slist
    AutoExecUtils.saveOutput(out)
